use std::cell::Cell;
use std::rc::Rc;
use std::{cell::RefCell, ops::Deref};

use objc2::msg_send;
use objc2::rc::Id;
use objc2_app_kit::{NSView, NSWindow};
use objc2_foundation::NSMutableAttributedString;

use super::Window;

#[derive(Clone, Copy)]
pub struct WinRef<'a>(pub &'a NSWindow, pub &'a NSView);
impl<'a> Deref for WinRef<'a> {
	type Target = NSWindow;
	fn deref(&self) -> &Self::Target {
		self.0
	}
}
impl<'a> AsRef<NSView> for WinRef<'a> {
	fn as_ref(&self) -> &NSView {
		self.1
	}
}
impl<'a> types::WinRef<WinRaw> for WinRef<'a> {
	fn raw(&self) -> Option<WinRaw> {
		use core::ffi::c_void;
		if let Some(view) = self.contentView() {
			let view = &*view as *const NSView;
			let view = view as *const c_void;
			let view = view as *mut c_void;
			return Some(WinRaw::from(view));
		}
		None
	}
	fn show(&self) {
		let window = self.0;
		window.makeKeyAndOrderFront(Some(&window));
	}
	fn close(&self) {
		let window = self.0;
		window.close()
	}
	fn fresh(&self, _: &str) {
		let () = unsafe { msg_send![self.1, refresh] };
		// let view = self.1;
		// unsafe { view.setNeedsDisplay(true) };
	}
	fn size(&self) -> (u32, u32) {
		let factor: Factor = self.0.into();
		let frame = self.1.frame();
		let ret = (factor.to_u32(frame.size.width), factor.to_u32(frame.size.height));
		ret
	}
	fn density(&self) -> f32 {
		Factor::from(self.0).as_f32()
	}
}

#[derive(Debug)]
pub struct WinHandle(pub Id<XLoopWindow>);
impl types::WinHandle<Window> for WinHandle {
	fn with(&self, fun: impl FnOnce(WinRef<'_>)) {
		if let Some(view) = self.0.contentView() {
			fun(WinRef(&self.0, &view));
		}
	}
}

#[derive(Clone)]
pub struct Vars {
	rep: Rc<dyn types::WinRep<Window>>,
	refresh: Cell<bool>,
	marked_text: Rc<RefCell<Id<NSMutableAttributedString>>>,
}

mod event;
pub use event::*;

mod win;
pub use win::*;

mod view;
pub use view::*;

mod raw;
pub use raw::*;

mod factor;
pub use factor::*;
